package com.dhj.jvmstudy.chapter07;

/**
 * 字符串拼接操作
 */
public class StringTest5 {

    public static void main(String[] args) {
        test1();
        test2();
        test3();
        test4();
        test5();
        test6();
    }

    private static void test1() {
        String s1 = "a" + "b" + "c";  // 等同于"abc"
        // "abc"一定是放在字符串常量池中，将地址赋给s2
        String s2 = "abc";

        /**
         * 最终，.java编译成.class，再执行.class
         * String s1 = "abc";
         * String s2 = "abc";
         */
        System.out.println(s1 == s2);        // true
        System.out.println(s1.equals(s2));   // true

    }

    private static void test2() {
        String s1 = "javaEE";
        String s2 = "hadoop";

        String s3 = "javaEEhadoop";
        String s4 = "javaEE"+"hadoop";  // 编译期优化

        // 如果拼接符号的前后出现了变量，则相当于在堆空间中new String()，具体的内容为拼接的结果：javaEEhadoop
        String s5 = s1 + "hadoop";
        String s6 = "javaEE"+s2;
        String s7 = s1 + s2;

        System.out.println(s3 == s4);  // true
        System.out.println(s3 == s5);  // false
        System.out.println(s3 == s6);  // false
        System.out.println(s3 == s7);  // false
        System.out.println(s5 == s6);  // false
        System.out.println(s5 == s7);  // false
        System.out.println(s6 == s7);  // true

        // intern()：判断字符串常量池中是否存在javaEEhadoop值，如果存在，则返回常量池中javaEEhadoop的地址。
        // 如果字符串常量池中不存在javaEEhadoop，则在常量池中加载一份javaEEhadoop，并返回此对象的地址。
        String s8 = s6.intern();
        System.out.println(s3 == s8);  // true

    }

    private static void test3() {
        String s1 = "a";
        String s2 = "b";
        String s3 = "ab";

        /**
         * 如下的s1+s2的执行细节
         * StringBuilder s = new StringBuilder();
         * s.append("a");
         * s.append("b");
         * s.toString();   -->   约等于 new String("ab")
         */
        String s4 = s1 + s2;

        System.out.println(s3 == s4);  // false
    }

    /**
     * 字符串拼接操作不一定使用的是StringBuilder
     * 如果拼接符号左右两边都是字符串常量或者常量引用，则仍然是使用编译期优化，即非StringBuilder的方式
     *
     * 针对于final修饰类，方法，基本数据类型，引用数据类型的量的结构时，能使用上final的时候建议使用上。
     */
    private static void test4() {
        final String s1 = "a";
        final String s2 = "b";
        String s3 = "ab";
        /**
         * 这里虽然也是变量的拼接，但是这里的变量实际上是final常量
         */
        String s4 = s1 + s2;
        System.out.println(s4 == s3);    // true
    }

    private static void test5() {
        String s1 = "javaEEhadoop";
        String s2 = "javaEE";
        String s3 = s2 +"hadoop";
        System.out.println(s1 == s3);  //false
        final String s4 = "javaEE";    //s4:常量
        String s5 = s4 + "hadoop";
        System.out.println(s1 == s5); //true
    }

    /**
     * 体会执行效率：通过StringBuilder的append()的方式添加字符串的效率要远高于使用string的字符串拼接方式！
     * 详情：①StringBuilder.的append()的方式：自始至终中只创建过一个stringBuilder的对象
     *       使用string的字符串拼接方式：创建过多个stringBuilder和string的对象
     *      ②使用string的字符串拼接方式：内存中由于创建了较多stringBuilder和string的对象，内存占用更大；如果进行GC，也需要花费额外的时间。
     *
     * 改进的空间：在实际开发中，如果基本确定要添加的字符串长度不高于某个限定值highLevel的情况下，建议使用构造器实例化
     *            StringBuilder s = new StringBuilder(highLevel);   // new char[highLevel]
     */
    private static void test6(){
        long start = System.currentTimeMillis();
        method1(100000);
        long end = System.currentTimeMillis();
        /**
         * method1()：4014ms
         * method2()：7ms
         */
        System.out.println("耗时："+(end-start)+"ms");
    }

    private static void method1(int highLevel){
        String str = "";
        for(int i=0; i<highLevel; i++){
            str = str + "a";  // 每次循环都会创建一个StringBuilder
        }
    }


    private static void method2(int highLevel){
        // 只需要创建一个StringBuilder
        StringBuilder str = new StringBuilder();
        for(int i=0; i<highLevel; i++){
            str.append("a");
        }
    }

}
